
//
//  PQCgenKAT_kem.c
//
//  Created by Bassham, Lawrence E (Fed) on 8/29/17.
//  Copyright © 2017 Bassham, Lawrence E (Fed). All rights reserved.
//

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "rng.h"
#include "api.h"

#define	MAX_MARKER_LEN		50
#define KAT_SUCCESS          0
#define KAT_FILE_OPEN_ERROR -1
#define KAT_DATA_ERROR      -3
#define KAT_CRYPTO_FAILURE  -4

int		FindMarker(FILE *infile, const char *marker);
int		ReadHex(FILE *infile, unsigned char *A, int Length, char *str);
void	fprintBstr(FILE *fp, char *S, unsigned char *A, unsigned long long L);

int
main()
{
   char                fn_req[32], fn_rsp[32];
   FILE                *fp_req, *fp_rsp;
   unsigned char       seed[48];
   unsigned char       entropy_input[48];
   unsigned char       ct[CRYPTO_CIPHERTEXTBYTES], ss[CRYPTO_BYTES],
            ss1[CRYPTO_BYTES];
   int                 count;
   int                 done;
   unsigned char       pk[CRYPTO_PUBLICKEYBYTES], sk[CRYPTO_SECRETKEYBYTES];
   int                 ret_val;

   // Create the REQUEST file
   sprintf(fn_req, "../../../KAT/PQCkemKAT_%d_%d.req", (int)CRYPTO_SECRETKEYBYTES,
           N0);
   if ( (fp_req = fopen(fn_req, "w")) == NULL ) {
      printf("Couldn't open <%s> for write\n", fn_req);
      return KAT_FILE_OPEN_ERROR;
   }
   sprintf(fn_rsp, "../../../KAT/PQCkemKAT_%d_%d.rsp", (int)CRYPTO_SECRETKEYBYTES,
           N0);
   if ( (fp_rsp = fopen(fn_rsp, "w")) == NULL ) {
      printf("Couldn't open <%s> for write\n", fn_rsp);
      return KAT_FILE_OPEN_ERROR;
   }

   for (int i=0; i<48; i++)
      entropy_input[i] = i;

   randombytes_init(entropy_input, NULL, 256);
   for (int i=0; i<100; i++) {
      fprintf(fp_req, "count = %d\n", i);
      randombytes(seed, 48);
      fprintBstr(fp_req, "seed = ", seed, 48);
      fprintf(fp_req, "pk =\n");
      fprintf(fp_req, "sk =\n");
      fprintf(fp_req, "ct =\n");
      fprintf(fp_req, "ss =\n\n");
   }
   fclose(fp_req);

   //Create the RESPONSE file based on what's in the REQUEST file
   if ( (fp_req = fopen(fn_req, "r")) == NULL ) {
      printf("Couldn't open <%s> for read\n", fn_req);
      return KAT_FILE_OPEN_ERROR;
   }

   fprintf(fp_rsp, "# %s\n\n", CRYPTO_ALGNAME);
   done = 0;
   do {
      if ( FindMarker(fp_req, "count = ") )
         fscanf(fp_req, "%d", &count);
      else {
         done = 1;
         break;
      }
      fprintf(fp_rsp, "count = %d\n", count);

      if ( !ReadHex(fp_req, seed, 48, "seed = ") ) {
         printf("ERROR: unable to read 'seed' from <%s>\n", fn_req);
         return KAT_DATA_ERROR;
      }
      fprintBstr(fp_rsp, "seed = ", seed, 48);

      randombytes_init(seed, NULL, 256);

      // Generate the public/private keypair
      if ( (ret_val = crypto_kem_keypair(pk, sk)) != 0) {
         printf("crypto_kem_keypair returned <%d>\n", ret_val);
         return KAT_CRYPTO_FAILURE;
      }
      fprintBstr(fp_rsp, "pk = ", pk, CRYPTO_PUBLICKEYBYTES);
      fprintBstr(fp_rsp, "sk = ", sk, CRYPTO_SECRETKEYBYTES);

      if ( (ret_val = crypto_kem_enc(ct, ss, pk)) != 0) {
         printf("crypto_kem_enc returned <%d>\n", ret_val);
         return KAT_CRYPTO_FAILURE;
      }
      fprintBstr(fp_rsp, "ct = ", ct, CRYPTO_CIPHERTEXTBYTES);
      fprintBstr(fp_rsp, "ss = ", ss, CRYPTO_BYTES);

      fprintf(fp_rsp, "\n");

      if ( (ret_val = crypto_kem_dec(ss1, ct, sk)) != 0) {
         printf("crypto_kem_dec returned <%d>\n", ret_val);
         return KAT_CRYPTO_FAILURE;
      }

      if ( memcmp(ss, ss1, CRYPTO_BYTES) ) {
         printf("crypto_kem_dec returned bad 'ss' value\n");
         return KAT_CRYPTO_FAILURE;
      }

   } while ( !done );

   fclose(fp_req);
   fclose(fp_rsp);

   return KAT_SUCCESS;
}



//
// ALLOW TO READ HEXADECIMAL ENTRY (KEYS, DATA, TEXT, etc.)
//
//
// ALLOW TO READ HEXADECIMAL ENTRY (KEYS, DATA, TEXT, etc.)
//
int
FindMarker(FILE *infile, const char *marker)
{
   char	line[MAX_MARKER_LEN];
   int		i, len;
   int curr_line;

   len = (int)strlen(marker);
   if ( len > MAX_MARKER_LEN-1 )
      len = MAX_MARKER_LEN-1;

   for ( i=0; i<len; i++ ) {
      curr_line = fgetc(infile);
      line[i] = curr_line;
      if (curr_line == EOF )
         return 0;
   }
   line[len] = '\0';

   while ( 1 ) {
      if ( !strncmp(line, marker, len) )
         return 1;

      for ( i=0; i<len-1; i++ )
         line[i] = line[i+1];
      curr_line = fgetc(infile);
      line[len-1] = curr_line;
      if (curr_line == EOF )
         return 0;
      line[len] = '\0';
   }

   // shouldn't get here
   return 0;
}

//
// ALLOW TO READ HEXADECIMAL ENTRY (KEYS, DATA, TEXT, etc.)
//
int
ReadHex(FILE *infile, unsigned char *A, int Length, char *str)
{
   int			i, ch, started;
   unsigned char	ich;

   if ( Length == 0 ) {
      A[0] = 0x00;
      return 1;
   }
   memset(A, 0x00, Length);
   started = 0;
   if ( FindMarker(infile, str) )
      while ( (ch = fgetc(infile)) != EOF ) {
         if ( !isxdigit(ch) ) {
            if ( !started ) {
               if ( ch == '\n' )
                  break;
               else
                  continue;
            } else
               break;
         }
         started = 1;
         if ( (ch >= '0') && (ch <= '9') )
            ich = ch - '0';
         else if ( (ch >= 'A') && (ch <= 'F') )
            ich = ch - 'A' + 10;
         else if ( (ch >= 'a') && (ch <= 'f') )
            ich = ch - 'a' + 10;
         else // shouldn't ever get here
            ich = 0;

         for ( i=0; i<Length-1; i++ )
            A[i] = (A[i] << 4) | (A[i+1] >> 4);
         A[Length-1] = (A[Length-1] << 4) | ich;
      } else
      return 0;

   return 1;
}

void
fprintBstr(FILE *fp, char *S, unsigned char *A, unsigned long long L)
{
   unsigned long long  i;

   fprintf(fp, "%s", S);

   for ( i=0; i<L; i++ )
      fprintf(fp, "%02X", A[i]);

   if ( L == 0 )
      fprintf(fp, "00");

   fprintf(fp, "\n");
}

